#ifndef OFUNCTOR_H
#define OFUNCTOR_H

class Entity;

//#define FUNC(arg1,arg2,...) (void (Entity::*) ( __VA_ARGS__ )) (void (arg1::*) ( __VA_ARGS__ )) & arg1::arg2

	class Functor{
		public:
			Functor(Entity *point):
				obj(point)
			{}
			virtual ~Functor(){}
		
			virtual void operator() () {}
		
			template <class C>
			void operator() (C c){}
			
			template <class C, class D>
			void operator() (C c, D d){}
			
			template <class C, class D, class E>
			void operator() (C c, D d, E e){}
			
			Entity *obj;
	};
	
	class SpecificFunctor0a: public Functor{
		public:
			SpecificFunctor0a(Entity* o, void (Entity::*p)(void)):
				Functor(o),
				func(p)
			{}
			virtual ~SpecificFunctor0a(){}
			
			void (Entity::*func) (void);
			
			void operator() (void) {((*obj).*func)();}
	};
	
	template <class C>
	class SpecificFunctor1a: public Functor{
		public:
			SpecificFunctor1a(Entity* o, void (Entity::*p)(C)):
				Functor(o),
				func(p)
			{}
			virtual ~SpecificFunctor1a(){}
	
		void (Entity::*func) (C);
		C defArg1;
		
		void operator() (C c){((*obj).*func)(c);}
		void operator() (){((*obj).*func)(defArg1);}
	};
	
	template <class C, class D>
	class SpecificFunctor2a: public Functor{
		public:
			SpecificFunctor2a(Entity* o, void (Entity::*p)(C,D)):
				Functor(o),
				func(p)
			{}
			virtual ~SpecificFunctor2a(){}
	
		void (Entity::*func) (C,D);
		C defArg1;
		D defArg2;
		
		void operator() (){((*obj).*func)(defArg1,defArg2);}
		void operator() (C c,D d){((*obj).*func)(c,d);}
	};
	
	template <class C, class D, class E>
	class SpecificFunctor3a: public Functor{
		public:
			SpecificFunctor3a(Entity* o, void (Entity::*p)(C,D,E)):
				Functor(o),
				func(p)
			{}
			virtual ~SpecificFunctor3a(){}
	
		void (Entity::*func) (C,D,E);
		C defArg1;
		D defArg2;
		E defArg3;
		
		void operator() (){((*obj).*func)(defArg1,defArg2,defArg3);}
		void operator() (C c,D d,E e){((*obj).*func)(c,d,e);}
	};
	
	template <class C, class D, class E, class F>
	class SpecificFunctor4a: public Functor{
		public:
			SpecificFunctor4a(Entity* o, void (Entity::*p)(C,D,E,F)):
				Functor(o),
				func(p)
			{}
	
		void (Entity::*func) (C,D,E,F);
		C defArg1;
		D defArg2;
		E defArg3;
		F defArg4;
		
		void operator() (){((*obj).*func)(defArg1,defArg2,defArg3,defArg4);}
		void operator() (C c,D d,E e,F f){((*obj).*func)(c,d,e,f);}
	};





	class FunctorContainer{
			public:
			
				~FunctorContainer(){
					for (unsigned int i = 0; i < functions.size(); ++i){
						functions[i]->obj->removeFunctorLink(this);
						delete functions[i];
					}
					functions.clear();
				}
				
				
				
				std::vector <Functor *> functions;
				
					//=====================================================
					//=====================================================
				
					Functor * getFunction (Entity *ent, void (Entity::*p)() ) {
						for (unsigned int i = functions.size() - 1; i >= 0; --i){
							if (functions[i]->obj == ent && ((SpecificFunctor0a *) functions[i])->func == p){
								return functions[i];
							}
						}
						return NULL;
					}
					
					
					//=====================================================
					//=====================================================
				
					void removeFunction (Entity *ent) {
						for (unsigned int i = 0; i < functions.size(); ++i){
							if (functions[i]->obj == ent){functions.erase(functions.begin() + i);--i;}
						}
					}
					
					//=====================================================
					//=====================================================
					
					void removeFunction (Entity *ent, void (Entity::*p)() ){
						for (unsigned int i = 0; i < functions.size(); ++i){
							if (functions[i]->obj == ent && ((SpecificFunctor0a *) functions[i])->func == p){
								functions.erase(functions.begin() + i);break;
							}
						}
					}
					
					template <class C>
					void removeFunction (Entity *ent, void (Entity::*p)(C) ){
						for (unsigned int i = 0; i < functions.size(); ++i){
							if (functions[i]->obj == ent && ((SpecificFunctor1a<C> *) functions[i])->func == p){
								functions.erase(functions.begin() + i);break;
							}
						}
					}
					
					template <class C, class D>
					void removeFunction (Entity *ent, void (Entity::*p)(C,D) ){
						for (unsigned int i = 0; i < functions.size(); ++i){
							if (functions[i]->obj == ent && ((SpecificFunctor2a<C,D> *) functions[i])->func == p){
								functions.erase(functions.begin() + i);break;
							}
						}
					}
					
					template <class C, class D, class E>
					void removeFunction (Entity *ent, void (Entity::*p)(C,D,E) ){
						for (unsigned int i = 0; i < functions.size(); ++i){
							if (functions[i]->obj == ent && ((SpecificFunctor3a<C,D,E> *) functions[i])->func == p){
								functions.erase(functions.begin() + i);break;
							}
						}
					}
					
					template <class C, class D, class E, class F>
					void removeFunction (Entity *ent, void (Entity::*p)(C,D,E,F) ){
						for (unsigned int i = 0; i < functions.size(); ++i){
							if (functions[i]->obj == ent && ((SpecificFunctor4a<C,D,E,F> *) functions[i])->func == p){
								functions.erase(functions.begin() + i);break;
							}
						}
					}
					
					//=====================================================
					//=====================================================
					
				
				void addFunction (Functor *s){functions.push_back(s);}
				
				void call (){for (unsigned int i = 0; i < functions.size(); ++i){(* ( functions[i]))();}}
				
				template <class C>
				void call (C c){for (unsigned int i = 0; i < functions.size(); ++i){(* ((SpecificFunctor1a<C> *) functions[i]))(c);}}
				
				template <class C, class D>
				void call (C c, D d){
					for (unsigned int i = 0; i < functions.size(); ++i){(* ((SpecificFunctor2a<C,D> *) functions[i]))(c,d);}}
				
				template <class C, class D, class E>
				void call (C c, D d, E e){for (unsigned int i = 0; i < functions.size(); ++i){(* ((SpecificFunctor3a<C,D,E> *) functions[i]))(c,d,e);}}
				
				template <class C, class D, class E, class F>
				void call (C c, D d, E e, F f){for (unsigned int i = 0; i < functions.size(); ++i){(* ((SpecificFunctor4a<C,D,E,F> *) functions[i]))(c,d,e,f);}}
		};
	
	
#endif
